import logging
import os
import pdb
import pysolr
from flask.ext.openid import OpenID
from flask import Flask
from flask import render_template, flash, redirect, session, url_for, request, g
from flask import jsonify
from flask.ext.login import LoginManager
from flask.ext.login import login_user, logout_user, login_required, current_user
from forms import LoginForm, UserProfile, AddActivityForm, enabled_sports_for_user, AddCompetitionForm,DietaFilter
from models import *
from database import db
from config import basedir
from flaskext.uploads import (UploadSet, configure_uploads, IMAGES,
                              UploadNotAllowed)

def __import_blueprint(blueprint_str):
    split = blueprint_str.split('.')
    module_path = '.'.join(split[0: len(split) - 1])
    variable_name = split[-1]
    mod = __import__(module_path, fromlist=[variable_name])
    return getattr(mod, variable_name)


def config_str_to_obj(cfg):
    if isinstance(cfg, basestring):
        module = __import__('config', fromlist=[cfg])
        return getattr(module, cfg)
    return cfg


def app_factory(config, app_name=None, blueprints=None):
    app_name = app_name or __name__
    app = Flask(app_name)

    config = config_str_to_obj(config)
    configure_app(app, config)
    configure_logger(app, config)
    configure_blueprints(app, blueprints or config.BLUEPRINTS)
    configure_login(app)
    configure_error_handlers(app)
    configure_database(app)
    configure_context_processors(app)
    configure_template_filters(app)
    configure_extensions(app)
    configure_before_request(app)
    configure_views(app)
    configure_upload(app)
    configure_solr(app, config)
    
    return app

def configure_solr(app, config):
    app.solr = pysolr.Solr(config.SOLR_URL)
    
def configure_upload(app):
    uploaded_photos = UploadSet('photos', IMAGES)
    configure_uploads(app, uploaded_photos)

def configure_login(app):
    login_manager = LoginManager()
    login_manager.init_app(app)
    login_manager.login_view = 'login'
    
    @login_manager.user_loader
    def load_user(id):
        return User.query.get(int(id))
    

def configure_app(app, config):
    """Loads configuration class into flask app"""
    app.config.from_object(config)
    app.config.from_envvar("APP_CONFIG", silent=True)  # available in the server


def configure_logger(app, config):
    logfile = config.LOG_FILENAME

    # Create a file logger since we got a logdir
    lfile = logging.FileHandler(filename=logfile)
    formatter = logging.Formatter(config.LOG_FORMAT)
    lfile.setFormatter(formatter)
    lfile.setLevel(config.LOG_LEVEL)
    app.logger.addHandler(lfile)
    app.logger.info("Logger started")


def configure_blueprints(app, blueprints):
    """Registers all blueprints set up in config.py"""
    for blueprint_config in blueprints:
        blueprint, kw = None, {}

        if (isinstance(blueprint_config, basestring)):
            blueprint = blueprint_config
        elif (isinstance(blueprint_config, tuple)):
            blueprint = blueprint_config[0]
            kw = blueprint_config[1]
        else:
            print "Error in BLUEPRINTS setup in config.py"
            print "Please, verify if each blueprint setup is either a string or a tuple."
            exit(1)

        blueprint = __import_blueprint(blueprint)
        app.register_blueprint(blueprint, **kw)


def configure_error_handlers(app):
    @app.errorhandler(403)
    def forbidden_page(error):
        """
        The server understood the request, but is refusing to fulfill it.
        Authorization will not help and the request SHOULD NOT be repeated.
        If the request method was not HEAD and the server wishes to make public
        why the request has not been fulfilled, it SHOULD describe the reason for
        the refusal in the entity. If the server does not wish to make this
        information available to the client, the status code 404 (Not Found)
        can be used instead.
        """
        return render_template("access_forbidden.html"), 403


    @app.errorhandler(404)
    def page_not_found(error):
        """
        The server has not found anything matching the Request-URI. No indication
        is given of whether the condition is temporary or permanent. The 410 (Gone)
        status code SHOULD be used if the server knows, through some internally
        configurable mechanism, that an old resource is permanently unavailable
        and has no forwarding address. This status code is commonly used when the
        server does not wish to reveal exactly why the request has been refused,
        or when no other response is applicable.
        """
        return render_template("page_not_found.html"), 404


    @app.errorhandler(405)
    def method_not_allowed_page(error):
        """
        The method specified in the Request-Line is not allowed for the resource
        identified by the Request-URI. The response MUST include an Allow header
        containing a list of valid methods for the requested resource.
        """
        return render_template("method_not_allowed.html"), 405


    @app.errorhandler(500)
    def server_error_page(error):
        return render_template("server_error.html"), 500


def configure_database(app):
    """Database configuration should be set here"""
    # uncomment for sqlalchemy support
    # from database import db
    db.app = app
    db.init_app(app)


def configure_context_processors(app):
    """Modify templates context here"""
    pass


def configure_template_filters(app):
    """Configure filters and tags for jinja"""
    pass


def configure_extensions(app):
    """Configure extensions like mail and login here"""
    pass


def configure_before_request(app):
    @app.before_request
    def before_request():
        g.user = current_user

def configure_views(app):
    
    oid = OpenID(app, os.path.join(basedir, 'tmp'))


    """Add some simple views here like index_view"""
    @app.route("/")
    def index_view():
        return render_template("index.html")
    
    @app.route('/')
    @app.route('/index')
    def index():
        user = g.user
        return render_template('index.html',
            title = 'Home',
            user = user)
    
    @app.route("/dashboard")
    @login_required
    def userDashBoard():
        return render_template("dashboard.html")
    
    @app.route("/maps")
    @login_required
    def maps():
        return render_template("maps.html")

    @app.route("/competencias")
    @login_required
    def competencias():
	
        hoy = datetime.utcnow()
        compet_user = Competencias.query.filter(Competencias.user.any(id=g.user.get_id()),Competencias.fecha>=hoy).all()

#        compet_otros = Competencias.query.filter(Competencias.user.any(id!=g.user.get_id())).all()
		
        next_compet = Competencias.query.filter(Competencias.fecha>=hoy).all()
		
        def get_compet_deporte(deporte_id):
            deporte_compet = Deportes.query.get(deporte_id)
            return deporte_compet.nombre

#        def get_usuario(user_id):
#            usuario = User.query.get(user_id)
#            if(usuario.nombre != "") 
#                return usuario.nombre + " " + usuario.apellido
#            else
#                return usuario.apodo
			
        return render_template("competenciasUser.html",competencias_del_usuario=compet_user,next_compet=next_compet,get_compet_deporte=get_compet_deporte)
    
    @app.route("/productos")
    @login_required
    def productos():
        return render_template("productos.html")
    
    @app.route("/productosChango")
    @login_required
    def productosChango():
        return render_template("productosChango.html")

    @app.route('/addfriend', methods = ['POST'])
    @login_required
    def addfriend():
		try:
			# Verifico que la lista exista en bd
			targetListId = request.form['list_id']
			myNewList = List.query.get(targetListId)
			if not(myNewList):
				# No se encontro la lista en bd
				return jsonify({'result': '1'})
			
			# Obtengo parametros
			extid = request.form['external_id']
			authprovid = request.form['auth_provider_id']
			
			# Verifico si el amigo ya esta en bd
			friendList = Friend.query.filter_by(external_id = extid, auth_provider_id = authprovid).all()
			if len(friendList) == 1:
				# Verifico si ya esta suscripto a la lista a la que lo quiero agregar
				myFriend = friendList[0]
				for list in myFriend.lists:
					if int(list.id) == int(targetListId):
						app.logger.info('Su amigo '+str(myFriend)+' ya esta en la lista '+str(myNewList))
						return jsonify({'result': '2'})
			else:
				# Obtengo parametros del amigo y lo creo en bd
				fname = request.form['nombre']
				imgurl = request.form['image_url']
				currentUserId = g.user.get_id();
				myFriend = Friend(nombre = fname, image_url = imgurl, external_id = extid, auth_provider_id = authprovid, user_id = currentUserId )
				db.session.add(myFriend)
			
			# Lo agrego a la lista
			app.logger.info('Agregando amigo '+str(myFriend)+' a lista '+str(myNewList))
			myFriend.lists.append(myNewList);
			db.session.commit()
			
			return jsonify({'result': '0'})
		except:
			return jsonify({'result': '3'})
		

    @app.route("/circulos")
    @login_required
    def circulos():
		# Obtengo los amigos que estan en las listas del usuario logueado
		currentUserId = g.user.get_id()
		friends = Friend.query.filter_by(user_id = currentUserId).all()
		friendList = []
		for f in friends:
			# Armo un csv con los ids de las listas a las que esta suscripto el amigo
			listsIdsCsv = ""
			for l in f.lists:
				listsIdsCsv += ","+str(l.id)
			listsIdsCsv = listsIdsCsv[1:]
			
			# Formateo la info de mi amigo y la agrego al diccionario de salida
			friendDict = {'name': f.nombre, 'image_url': f.image_url, 'url': '', 'external_id': f.external_id, 'auth_provider_id': f.auth_provider_id, 'listsIds': listsIdsCsv}
			friendList.append(friendDict)
		
		# Armo la lista de contactos del usuario (por ahora es la lista de usuarios de sistema)
		contactList = []
		contacts = User.query.filter(User.id != currentUserId).all()
		for c in contacts:
			contactDict = {'name': c.apodo, 'image': c.image_url, 'url': '', 'external_id': c.external_id, 'auth_provider_id': c.auth_provider_id}
			contactList.append(contactDict)
		
		return render_template("circulosUser.html",contacts = contactList, friends = friendList)

    @app.route("/dietaCalendario")
    @login_required    
    def dietaCalendario():
        return render_template("dietaCalendario.html")


    @app.route("/dietaObtener", methods = ['GET', 'POST'])
    @login_required    
    def dietaObtener():
        form = DietaFilter(request.form)
        
        if request.method == 'GET':
            queryResult = app.solr.search(form.ingredientes.data)
        if request.method == 'POST':
            queryResult = app.solr.search(form.ingredientes.data)
        
        return render_template("dietaObtener.html",form=form,results=queryResult)


        

    @app.route("/dietaDiaria")
    @login_required    
    def dietaDiaria():
        return render_template("dietaDiaria.html")

    @app.route("/rutina")
    @login_required
    def rutina():
        return render_template("rutina.html")

    @app.route("/ejercicio")
    @login_required
    def ejercicio():
        return render_template("ejercicio.html")
    
    @app.route("/agregarActividad", methods = ['GET', 'POST'])
    @login_required
    def agregarActividad():
        form = AddActivityForm(request.form)
        
        if request.method == 'POST':        
            if form.validate():
                userActivity = UserActivity()
                form.populate_obj(userActivity)
                userActivity.deporte_id = request.form['deportes']
                userActivity.fecha = request.form['fecha']
                userActivity.distancia = userActivity.distancia.replace(",",".")
                userActivity.duracion = userActivity.duracion.replace(",",".")
                #flash(auxDistancia)
                modify_user = User.query.get(g.user.get_id())
                modify_user.actividades.append(userActivity)
                db.session.add(userActivity)           
                db.session.commit()
                flash('Actividad agregada')
        
        return render_template("agregarActividad.html",form=form)
    
    @app.route("/trackearActividad", methods = ['GET', 'POST'])
    @login_required
    def trackearActividad():
        deportes_del_usuario = Deportes.query.filter(Deportes.user.any(id=g.user.get_id())).all()
        return render_template("trackearActividad.html",deportes_del_usuario=deportes_del_usuario)
    
    @app.route("/listarActividades", methods = ['GET', 'POST'])
    @login_required
    def listarActividades():
        activ_del_usuario = UserActivity.query.filter(UserActivity.user.has(id=g.user.get_id())).all()
        activ_del_usuario_tracking = UserActivityWithSensor.query.filter_by(user_id=g.user.get_id()).all()
        
        def get_deporte(deporte_id):
            deporte = Deportes.query.get(deporte_id)
            return deporte.nombre
            
        def get_tracking(useractivity_id):
            count_tracking = UserActivityWithSensor.query.filter_by(useractivity_id=useractivity_id).count()
            if count_tracking > 0:
                return True
            return False
            
        return render_template("listarActividades.html",activ_del_usuario=activ_del_usuario,
                get_deporte=get_deporte,activ_del_usuario_tracking=activ_del_usuario_tracking,get_tracking=get_tracking)
	
    @app.route("/agregarCompetencia", methods = ['GET', 'POST'])
    @login_required
    def agregarCompetencia():
        form = AddCompetitionForm(request.form)
        if request.method == 'POST':        
            #if form.validate():
                competencia = Competencias()
                form.populate_obj(competencia)

                competencia.deporte_id = request.form['deportes']
                competencia.fecha = request.form['fecha']

                db.session.add(competencia)           
                db.session.commit()
                flash('Competencia agregada')
        
        return render_template("agregarCompetencia.html",form=form)
    
	
    @app.route('/upload', methods=['GET', 'POST'])
    def upload():
        photos = UploadSet('photos', IMAGES)
        if request.method == 'POST':
            photo = request.files.get('photo')
            try:
                uploaded_photos = UploadSet('photos', IMAGES)
                filename = uploaded_photos.save(photo)
                user = User.query.get(g.user.get_id())
                user.headshot = uploaded_photos.url(filename)
                db.session.commit()
                flash("Foto guardada.")
            except UploadNotAllowed:
                flash("The upload was not allowed")
            

        return redirect(url_for('profile'))

    @app.route("/profile", methods = ['GET', 'POST'])
    @login_required
    def profile():
        form = UserProfile(request.form)
        deportes = Deportes.query.all()
        deportes_del_usuario = Deportes.query.filter(Deportes.user.any(id=g.user.get_id())).all()
        
        if request.method == 'GET':
            form.apodo.data = g.user.get_apodo()
            form.nombre.data = g.user.get_nombre()
            form.apellido.data = g.user.get_apellido()
            form.email.data = g.user.get_email()
            form.direccion.data = g.user.get_direccion()
            form.fechaNacimiento.data = g.user.get_fechaNacimiento()
            
        if request.method == 'POST':
            #TODO hay un bug aca, si lo valida no actualiza la info.. no se porque        
            #if form.validate():
                modify_user = User.query.get(g.user.get_id())
                modify_user.nombre = form.nombre.data
                modify_user.apellido = form.apellido.data
                modify_user.direccion = form.direccion.data
                modify_user.fechaNacimiento = form.fechaNacimiento.data
                
                lista_deportes_seleccionadas = request.form.getlist('deportesList')
                lista_deportes_aux = []
                for nombre_deportes in lista_deportes_seleccionadas:
                    deportes_aux = Deportes.query.filter_by(nombre=nombre_deportes).first()
                    lista_deportes_aux.append(deportes_aux)
                    
                modify_user.deportes = lista_deportes_aux
                
                db.session.commit()
                form.apodo.data = g.user.get_apodo()
                form.email.data = g.user.get_email()
                deportes = Deportes.query.all()
                deportes_del_usuario = Deportes.query.filter(Deportes.user.any(id=g.user.get_id())).all()
                
        return render_template('profile.html', form=form, deportes=deportes, deportes_del_usuario=deportes_del_usuario)

    @app.route('/login', methods = ['GET', 'POST'])
    @oid.loginhandler
    def login():
        if g.user is not None and g.user.is_authenticated():
             return redirect(url_for('userDashBoard'))

        form = LoginForm(request.form)
        
        if request.method == 'POST':
            if form.validate():
                app.logger.info('Formulario valido')
                session['remember_me'] = form.remember_me.data
                return oid.try_login(form.openidTxt.data, ask_for = ['nickname', 'fullname', 'email'])
            
        return render_template('login.html', 
            title = 'Sign In',
            form = form,
            providers = app.config['OPENID_PROVIDERS'])

    
    @app.route('/logout')
    def logout():
        logout_user()
        session.pop('logged_in', None)
        return redirect(url_for('index'))


    @oid.after_login
    def after_login(resp):
        if resp.email is None or resp.email == "":
            flash(u'Invalid login. Please try again.')
            return redirect(url_for('login'))
        user = User.query.filter_by(email = resp.email).first()
        if user is None:
            nickname = resp.nickname
            if nickname is None or nickname == "":
                nickname = resp.email.split('@')[0]
            user = User(apodo = nickname, email = resp.email, role = ROLE_USER)
            db.session.add(user)
            db.session.commit()
        remember_me = False
        if 'remember_me' in session:
            remember_me = session['remember_me']
            session.pop('remember_me', None)
        login_user(user, remember = remember_me)
        session['logged_in'] = True
        return redirect(request.args.get('next') or url_for('userDashBoard'))
    
      

